/*
 * Copyright 2008 Neil Bacon
 * Distributed under the terms of the Mozilla Public License version 1.1
 * or the GNU General Public License version 3.
 */

package nb.xadd.operator.impl;

import nb.cache.CacheMap;
import nb.xadd.NotYetImplemented;
import nb.xadd.node.Node;
import nb.xadd.node.NodeFactory;
import nb.xadd.node.impl.ConstantValueNode;
import nb.xadd.node.impl.LinearValueNode;
import nb.xadd.node.impl.PolynomialValueNode;
import nb.xadd.node.impl.PolynomialValueNode.PolynomialCoefficients;

public class Addition extends AbstractBinaryOperator {

    protected Addition(NodeFactory nodeFactory, CacheMap<ApplyCacheKey, Node> applyCache) {
        super(nodeFactory, applyCache);
    }

    @Override
    protected boolean isNoOp(ConstantValueNode c) {
        return c.getConstValue() == 0.0;
    }

    @Override
    public Node executeConstantConstant(ConstantValueNode n1, ConstantValueNode n2) {
        if (isNoOp(n1)) {
            return n2;
        }
        if (isNoOp(n2)) {
            return n1;
        }
        return getNodeFactory().getConstantValueNode(n1.getConstValue() + n2.getConstValue());
    }

    @Override
    public Node executeConstantLinear(ConstantValueNode n1, LinearValueNode n2) {
        if (isNoOp(n1)) {
            return n2;
        }
        return getNodeFactory().getLinearValueNode(n1.getConstValue() + n2.getConstValue(), n2.getCoeff());
    }

    @Override
    public Node executeConstantPolynomial(ConstantValueNode n1, PolynomialValueNode n2) {
        if (isNoOp(n1)) {
            return n2;
        }
        return getNodeFactory().getPolynomialValueNode(n2.getNumberOfVariables(), n1.getConstValue() + n2.getConstValue(), n2.getCoeff());
    }

    @Override
    public Node executeLinearLinear(LinearValueNode n1, LinearValueNode n2) {
        // make n1 the node with the longer coeff array
        if (n1.getCoeff().length < n2.getCoeff().length) {
            LinearValueNode x = n1;
            n1 = n2;
            n2 = x;
        }
        double[] coeff = n1.getCopyOfCoeff();
        for (int i = 0; i < n2.getCoeff().length; i++) {
            coeff[i] += n2.getCoeff()[i];
        }
        return getNodeFactory().getLinearValueNode(n1.getConstValue() + n2.getConstValue(), coeff);
    }

    @Override
    public Node executeLinearPolynomial(LinearValueNode n1, PolynomialValueNode n2) {
        throw new NotYetImplemented();
    }

    @Override
    public Node executePolynomialPolynomial(PolynomialValueNode n1, PolynomialValueNode n2) {
        int numberOfVariables = Math.max(n1.getNumberOfVariables(), n2.getNumberOfVariables());
        // ensure n1 is arg with max polynomial order
        if (n1.getPolynomialOrder() < n2.getPolynomialOrder()) {
            PolynomialValueNode n = n1;
            n1 = n2;
            n2 = n;
        }
        final PolynomialCoefficients[] coeff = n1.getCopyOfCoeff();
        for (int i = 0; i < n2.getCoeff().length; i++) {
            coeff[i].addCoefficients(n2.getCoeff()[i]);
        }
        return getNodeFactory().getPolynomialValueNode(numberOfVariables, n1.getConstValue() + n2.getConstValue(), coeff);
    }

}
